/*********************************************************************************
 *     Copyright:  (C) 2022 Nikel
 *                 All rights reserved.
 *
 *    Filename:  sht30.c
 *    Description:  This file is SHT30 temperature and humidity sensor source file
 *
 *    Version:  1.0.0
 *    Author:  zhouyanlin <www.zhouyanlin1222@qq.com>
 *    ChangeLog:  1, Release initial version on "07/01/21 23:59:38"
 *
 ********************************************************************************/

#include <stdio.h>
#include "stm32l4xx_hal.h"
#include "sht30.h"

#define CONFIG_GPIO_I2C

#ifdef CONFIG_GPIO_I2C
#include "gpio_i2c.h"
#else
#include "i2c.h"
#endif

//#define CONFIG_SHT30_DEBUG

#ifdef CONFIG_SHT30_DEBUG
#define sht30_print(format,args...) printf(format, ##args)
#else
#define sht30_print(format,args...) do{} while(0)
#endif

static int sht30_send_cmd(SHT30_CMD cmd)
{
    uint8_t buf[2];

    buf[0] = cmd >> 8;
    buf[1] = cmd & 0xFF;

#ifdef CONFIG_GPIO_I2C
    return I2C_Master_Transmit(SHT30_ADDR_WR, (uint8_t*)buf, 2);
#else
    return HAL_I2C_Master_Transmit(&hi2c2, SHT30_ADDR_WR, (uint8_t*)buf, 2, 0xFFFF);
#endif
}


static void sht30_soft_reset(void)
{
	 sht30_send_cmd(SOFT_RESET_CMD);

     HAL_Delay(1);
}


static int sht30_single_shot_measurement(uint8_t *buf, uint8_t buf_size)
{
	uint16_t      cmd = HIGH_ENABLED_CMD;  /* High with clock stretching */
	uint8_t       rv;

	if( !buf || buf_size<SHT30_DATA_SIZE )
	{
		sht30_print("%s(): Invalid input arguments\n", __func__);
		return -1;
	}

	rv = sht30_send_cmd(cmd);
	if( rv )
	{
		sht30_print("ERROR: SHT30 send measurement command failure, rv=%d\n", rv);
		sht30_soft_reset();
		return -2;
	}

#ifdef CONFIG_GPIO_I2C
	rv = I2C_Master_Receive(SHT30_ADDR_RD, buf, SHT30_DATA_SIZE);
#else
	rv = HAL_I2C_Master_Receive(&hi2c2, SHT30_ADDR_RD, buf, SHT30_DATA_SIZE, 0xFFFF);
#endif
	if(rv)
	{
		sht30_print("ERROR: SHT30 read measurement result failure, rv=%d\n", rv);
		return -3;
	}

	return 0;
}


static uint8_t sht30_crc8(const uint8_t *data, int len)
{
     const uint8_t     POLYNOMIAL = 0x31;   /* SHT30 CRC8 Polynomial:  0x31=x8 + x5 + x4 + 1  */
     uint8_t           crc = 0xFF;          /* SHT30 CRC8 Initialization: 0xFF */
     int               i, j;

	 for (i=0; i<len; ++i)
	 {
     	crc ^= *data++;

     	for (j=0; j<8; ++j)
		{
        	crc = ( crc & 0x80 )? (crc << 1) ^ POLYNOMIAL: (crc << 1);
       	}
  	 }

	 return crc;
}


int SHT30_SampleData(float *temperature, float *humidity)
{
	uint8_t             buf[SHT30_DATA_SIZE];
	int                 rv;

	uint16_t            temp;
	uint16_t            humd;
	uint8_t             crc;

	if(!temperature || !humidity)
	{
		sht30_print("%s(): Invalid input arguments\n", __func__);
		return -1;
	}

	sht30_print("SHT30 start single short measurement\n");
	rv = sht30_single_shot_measurement(buf, SHT30_DATA_SIZE);
	if( rv )
	{
		sht30_print("SHT30 Single Short measurement failure, rv=%d\n", rv);
		return -2;
	}

#ifdef CONFIG_SHT30_DEBUG
	{
		int          i;

		sht30_print("SHT30 get %d bytes sample data: \n", SHT30_DATA_SIZE);
		for(i=0; i<SHT30_DATA_SIZE; i++)
		{
			sht30_print("0x%02x ", buf[i]);
		}
		sht30_print("\n");
	}
#endif

	/* byte[0-1] is temperature value, and byte[2] is temperature CRC */
	crc = sht30_crc8(buf, 2);
    sht30_print("SHT30 temperature Cal_CRC: [%02x] EXP_CRC: [%02x]\n", crc, buf[2]);
	if( crc != buf[2])
	{
		sht30_print("SHT30 measurement temperature got CRC error\n");
		return -3;
	}

	/* byte[3-4] is humidity value, and byte[5] is humidity CRC */
	crc = sht30_crc8(&buf[3], 2);
    sht30_print("SHT30 humidity Cal_CRC: [%02x] EXP_CRC: [%02x]\n", crc, buf[5]);
	if( crc != buf[5])
	{
		sht30_print("SHT30 measurement temperature got CRC error\n");
		return -4;
	}

	temp = (buf[0]<<8) | buf[1];
	humd = (buf[3]<<8) | buf[4];

	*temperature = -45 + 175*((float)temp/65535);
	*humidity = 100 * ((float)humd / 65535);

	return 0;
}

